#include <mutex>
#include "MqttClient.h"

// MqttClient 类的实现
MqttClient::MqttClient(GlobalConfig& config)
        : config(config), lastLoopTime(0) {
}

// 设置回调函数的方法，避免重复代码
void MqttClient::setCallback() {
    pubSubClient.setCallback([this](char* topic, byte* payload, unsigned int length) {
        this->callback(topic, payload, length);
    });
}

void MqttClient::subscribeToTopics() {
    std::string mqttTopicControl = config.getMqttTopicControl();
    debugLog("MQTT Subscribed to (topic=%s) \n", mqttTopicControl.c_str());
    pubSubClient.subscribe(mqttTopicControl.c_str());
}

const char* MqttClient::getStateText(int state) {
    const char* stateMessage;
    switch (state) {
        case -4:
            stateMessage = "Connection timed out";
            break;
        case -3:
            stateMessage = "Network error";
            break;
        case -2:
            stateMessage = "TLS handshake failed";
            break;
        case -1:
            stateMessage = "Connection failed";
            break;
        case 1:
            stateMessage = "Connection refused, unacceptable protocol version";
            break;
        case 2:
            stateMessage = "Connection refused, identifier rejected";
            break;
        case 3:
            stateMessage = "Connection refused, server unavailable";
            break;
        case 4:
            stateMessage = "Connection refused, bad user name or password";
            break;
        case 5:
            stateMessage = "Connection refused, not authorized";
            break;
        default:
            stateMessage = "Unknown error";
    }
    return stateMessage;
}

bool MqttClient::tryReconnect() {
    std::string sslCa = config.getMqttSslca();
    const char* ca_cert = sslCa.c_str();  // 获取 C 风格字符串表示
//    static const char ca_cert[] PROGMEM = R"EOF("+sslCa+")EOF";
    BearSSL::X509List serverTrustedCA(ca_cert);
    espClient.setTrustAnchors(&serverTrustedCA);

    int port = std::stoi(config.getMqttPort());
    std::string mqtt_broker = config.getMqttServer();
    std::string mqtt_username = config.getMqttUser();
    std::string mqtt_password = config.getMqttPassword();
    std::string mqttTopicControl = config.getMqttTopicControl();

    pubSubClient.setServer(mqtt_broker.c_str(), port);
    setCallback();

    while (!pubSubClient.connected()) {
        String client_id = "esp8266-client-" + String(WiFi.macAddress());
        debugLog("MQTT Connecting to Broker (client_id=%s).....\n", client_id.c_str());
        if (pubSubClient.connect(client_id.c_str(), mqtt_username.c_str(), mqtt_password.c_str())) {
            debugLog("MQTT Connected (state=ok) (client_id=%s)\n",client_id.c_str());
            subscribeToTopics();
//            pubSubClient.subscribe(mqttTopicControl.c_str());
//            pubSubClient.publish(mqttTopicControl.c_str(), "Hi EMQX I'm ESP8266 ^^");
        } else {
            debugLog("MQTT Connected Failed (state=%d) (stateText=%s)\n",pubSubClient.state(),getStateText(pubSubClient.state()));
//            char err_buf[128];
//            espClient.getLastSSLError(err_buf, sizeof(err_buf));
//            Serial.println(pubSubClient.state());
//            Serial.print("SSL error: ");
//            Serial.println(err_buf);
//            Serial.print("Error message: ");
//            Serial.println(getStateText(pubSubClient.state()));  // 输出具体错误信息
            delay(5000);
        }
    }
    return true;
}


void MqttClient::init() {
    if (GlobalConstants::syncTimeOk.load() > 1) {
        return;
    }

    try {
        const int port = std::stoi(config.getMqttPort());
        std::string mqttServer = config.getMqttServer();
        pubSubClient.setServer((const char *)mqttServer.c_str(), port);
        debugLog("MQTT server init %s:%d\n", mqttServer.c_str(), port);
    } catch (const std::invalid_argument& e) {
        debugLog("MQTT Invalid MQTT port: %s, Error: %s\n", config.getMqttPort().c_str(), e.what());
    } catch (const std::out_of_range& e) {
        debugLog("MQTT port out of range: %s, Error: %s\n", config.getMqttPort().c_str(), e.what());
    } catch (...) {
        debugLog("MQTT port error: %s\n", config.getMqttPort().c_str());
    }
    setCallback();
    GlobalConstants::syncTimeOk.store(2);
}

bool MqttClient::connect() {
    unsigned long currentTime = millis();
    if (currentTime - lastLoopTime >= loopInterval) {
        lastLoopTime = currentTime;
        debugLog("MQTT Failed to establish MQTT connection after multiple attempts.\n");

        return  tryReconnect();
    }
    return false;
}

void MqttClient::loop() {
    if (!GlobalConstants::wifiConnected) {
        return;
    }
    if (GlobalConstants::syncTimeOk.load() == 1) {
        init();
    }

    if (GlobalConstants::syncTimeOk.load() != 2) {
        return;
    }
    if (pubSubClient.connected()) {
        pubSubClient.loop();
    } else {
        connect();
    }
}

void MqttClient::callback(char* topic, byte* payload, unsigned int length) const {
    std::string message;
    for (unsigned int i = 0; i < length; i++) {
        message += static_cast<char>(payload[i]);
    }
    debugLog("MQTT Message arrived on (topic=%s)\n", topic, message.c_str());

    if (std::string(topic) == config.getMqttTopicControl()) {
        debugLog("MQTT Processing control (message=%s) \n", message.c_str());
    }
}